package com.chub.study.modules.aop;

import com.chub.study.modules.annotation.LogRecord;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;

import java.lang.reflect.Method;
import java.util.Objects;


/**
 * @author : chub
 * @version : V1.0.0
 * @fileName : LogRecordAop.java
 * @description : 记录方法交易时长的注解
 *
 * SpringBoot项目中使用AspectJ
 *      引入三方依赖：
 *          spring-aop：AOP核心功能，例如代理工厂等等。这里不需要引入。
 *          aspectjweaver：简单理解，支持切入点表达式等等。
 *          aspectjrt：简单理解，支持aop相关注解等等。
 *      由于aspectjweaver是包含aspectjrt，所以只需要引入aspectjweaver即可。
 *
 * @createTime : 2024-08-29 9:42
 */
@Aspect
@Component
@Slf4j
public class LogRecordAop {

    private final ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    // execution(public * *(..)) 定义任意公共方法的执行
    // execution(* set*(..)) 定义任何一个以"set"开始的方法的执行
    // execution(* com.xyz.service.AccountService.*(..)) 定义AccountService 接口的任意方法的执行
    // execution(* com.xyz.service.*.*(..)) 定义在service包里的任意方法的执行
    // execution(* com.xyz.service ..*.*(..)) 定义在service包和所有子包里的任意类的任意方法的执行
    // execution(* com.test.spring.aop.pointcutexp…JoinPointObjP2.*(…)) 定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行：
    // 说明：最靠近(..)的为方法名,靠近.∗(..))的为类名或者接口名,如上例的JoinPointObjP2.∗(..))

    // Pointcut定义时,还可以使用&&、∣∣、!运算符
    private final String POINT_CUT = "execution(* com.chub.study.modules.controller ..*.*(..)) || execution(* com.chub.study.modules.service.impl ..*.*(..)) ";


    // @Pointcut("@annotation(com.chub.study.modules.annotation.LogRecord)")  // 所有使用@LogRecord注解的地方
    @Pointcut(POINT_CUT)
    public void fun() {

    }

    @Before("fun()")
    public void before(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        LogRecord logRecord = method.getAnnotation(LogRecord.class);
        if (logRecord != null) {
            threadLocal.set(System.currentTimeMillis());
            String methodDesc = logRecord.methodDesc();
            String methodName = method.getName();
            // 如果用在Controller上，可以取到PostMapping的访问地址
            String requestUri = "";
            if (Objects.nonNull(method.getAnnotation(PostMapping.class))) {
                requestUri = method.getAnnotation(PostMapping.class).value()[0];
                log.info("requestURI({}) methodDesc({}) ==> 交易开始...)", requestUri, methodDesc);
            } else {
                log.info("methodName({}) methodDesc({}) ==> 交易开始...", methodName, methodDesc);
            }
        }
    }

    @After(("fun()"))
    public void after(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();

        LogRecord logRecord = method.getAnnotation(LogRecord.class);
        if (logRecord != null) {
            Long startTime = threadLocal.get();
            Long endTime = System.currentTimeMillis();
            Long costTime = endTime - startTime;

            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            String methodDesc = logRecord.methodDesc();
            String requestUri = "";
            if (Objects.nonNull(method.getAnnotation(PostMapping.class))) {
                requestUri = method.getAnnotation(PostMapping.class).value()[0];
                log.info("requestURI({}) methodName({}) methodDesc({}) ==> 交易结束,耗时 {}ms", requestUri, methodName, methodDesc, costTime);
            } else {
                log.info("methodName({}) methodDesc({}) ==> 交易结束,耗时 {}ms", methodName, methodDesc, costTime);
            }
        }
    }
}
